package com.cyf.github;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.springframework.util.StringUtils;
import java.nio.file.Paths;
import java.util.*;

/**
 * @author chenyifan
 * @create 2024-10-27 12:59
 */
public class CodeGenerator {

    // 子模块相对父模块的路径，如果不是多模块项目，则为空串
    public static String moduleRelativePath = "/mybatisplus/springboot-mybatisplus";
    public static String author = "chenyifan";

    // 数据库
    public static String datasourceType = "mysql";
    public static String datasourceUrl = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=UTF-8";
    public static String datasourceDriverClassName = "com.mysql.cj.jdbc.Driver";
    public static String datasourceUsername = "root";
    public static String datasourcePassword = "12345";
    public static String datasourceSchemaName = null;

    // 包
    public static String packageParent = "com.github.cyf";
    public static String packageModuleName = ""; // 模块名称
    public static String packageController = "controller";
    public static String packageService = "service";
    public static String packageServiceImpl = "service.impl";
    public static String packageMapper = "mapper";
    public static String packageEntity = "entity";

    // 策略配置
    // 是否删除表前缀
    public static boolean strategyIsRemoveTablePrefix = true;

    // 是否生成对应的文件
    public static boolean templateControllerGenerate = true;
    public static boolean templateServiceGenerate = true;
    public static boolean templateServiceImplGenerate = true;
    public static boolean templateMapperGenerate = true;
    public static boolean templateXmlGenerate = true;
    public static boolean templateEntityGenerate = true;


    /**
     * 代码生成器
     */
    private static AutoGenerator mpg = new AutoGenerator();

    /**
     * 包配置
     */
    private static PackageConfig pc = new PackageConfig();

    /**
     * 项目路径
     */
    private static String projectPath;

    public static void generate(String ...tables) {
        // System.getProperty("user.dir") 获取的是项目根路径，从父项目开始的
        projectPath = StringUtils.hasLength(moduleRelativePath) ?
                Paths.get(System.getProperty("user.dir")).toString() :
                Paths.get(System.getProperty("user.dir"), moduleRelativePath).toString();

        // 全局配置
        globalConfig();
        // 数据源配置
        dataSourceConfig();
        // 生成代码包配置
        packageConfig();
        // 策略配置
        strategyConfig(tables);
        // 模板配置
        templateConfig();
        // 自定义配置
        injectionConfig();
        // 设置引擎为 Freemarker
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        // 执行
        mpg.execute();
    }

    /**
     * 数据库配置
     */
    private static void dataSourceConfig() {
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.getDbType(datasourceType));
        dsc.setUrl(datasourceUrl);
        dsc.setDriverName(datasourceDriverClassName);
        dsc.setUsername(datasourceUsername);
        dsc.setPassword(datasourcePassword);
        // 设置模式名，例如 postgresql 的一个数据库有多个模式，每个模式下面才是表
        dsc.setSchemaName(datasourceSchemaName);
        mpg.setDataSource(dsc);
    }

    /**
     * 包配置
     * @return
     */
    private static void packageConfig() {
        // 父包名
        pc.setParent(packageParent);
        // 模块名称
        pc.setModuleName(packageModuleName);
        // controller 路径：parent/modelName/controller
        pc.setController(packageController);
        // entity 路径：parent/modelName/entity
        pc.setEntity(packageEntity);
        // service 路径：parent/modelName/service
        pc.setService(packageService);
        // mapper 路径：parent/modelName/mapper
        pc.setMapper(packageMapper);
        // serviceImpl 路径：parent/modelName/service/impl
        pc.setServiceImpl(packageServiceImpl);
        mpg.setPackageInfo(pc);
    }



    /**
     * 设置模板
     * 以 Controller 类为例，如何自定义模板？
     * 在 mybatis-plus-generator 包的 templates 目录下，有默认模板 controller.java.ftl
     * 如果需要重写，就在本项目的 rescource/templates 下自定义 controller.java.ftl 即可
     * 如果不需要创建 Controller 类，就调用 templateConfig.setController(null) 方法
     * @return
     */
    private static void templateConfig() {
        TemplateConfig templateConfig = new TemplateConfig();
        // 默认的 xml 生成的位置与 mapper 在一个目录，后面自定义到 resources/mapper 下
        templateConfig.setXml(null);
        if (!templateControllerGenerate) {
            templateConfig.setController(null);
        }
        if (!templateServiceGenerate) {
            templateConfig.setService(null);
        }
        if (!templateServiceImplGenerate) {
            templateConfig.setServiceImpl(null);
        }
        if (!templateMapperGenerate) {
            templateConfig.setMapper(null);
        }
        if (!templateEntityGenerate) {
            templateConfig.setEntity(null);
        }
        mpg.setTemplate(templateConfig);
    }

    /**
     * 自定义配置
     * @return
     */
    private static void injectionConfig() {
        // 自定义配置
        // 自定义属性，在模板中就可以使用，如 ${cfg.k1}
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("k1", "v1");
                this.setMap(map);
            }
        };

        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();

        if (templateXmlGenerate) {
            // 如果模板引擎是 freemarker
            // 默认模板在 在 mybatis-plus-generator 包的 templates 目录下，
            // 需要重写就在本项目的 rescource/templates 下自定义 mapper.xml.ftl
            String mapperXmlTemplatePath = ConstVal.TEMPLATE_XML + ".ftl";
            // 如果模板引擎是 velocity
            // String templatePath = ConstVal.TEMPLATE_XML + ".vm";

            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(mapperXmlTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 如果设置了表前缀，如 sys_user 设置了表前缀为 sys，那么此处 tableInfo.getEntityName() 为 User
                    String mapperXmlName = tableInfo.getEntityName() + "Mapper.xml";
                    return Paths.get(projectPath, "/src/main/resources/mapper/", pc.getModuleName(), mapperXmlName).toString();
                }
            });
        }
        // 自定义输出文件
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
    }


    private static void strategyConfig(String ...tables) {
        StrategyConfig strategy = new StrategyConfig();

        // 设置数据库表
        strategy.setInclude(tables);

        // 设置表前缀，生成实体时去掉表前缀
        if (strategyIsRemoveTablePrefix) {
            Set<String> tablePrefixs = new HashSet<>();
            for (String table : tables) {
                String[] splits = table.split("_");
                if (splits.length > 1) {
                    tablePrefixs.add(splits[0] + "_");
                }
            }
            strategy.setTablePrefix(tablePrefixs.toArray(new String[0]));
        }

        // 生成的实体类配置
        // 数据库表映射到实体的命名策略，驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        // 数据库表字段映射到实体的命名策略，驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 实体类属性中添加 @TableField 注解
        strategy.setEntityTableFieldAnnotationEnable(true);
        // 实体类是否为 lombok 模型（默认 false）
        strategy.setEntityLombokModel(true);
        // 你自己的父类实体,没有就不用设置!
        // strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        // 写于父类中的公共字段
        // strategy.setSuperEntityColumns("id");
        // 设置乐观锁字段名
        // strategy.setVersionFieldName("version");
        // 设置逻辑删除字段名
        // strategy.setLogicDeleteFieldName("is_deleted");

        // Controller 配置
        // 生成 @RestController 控制器
        strategy.setRestControllerStyle(true);
        // url驼峰转连字符，如 sys_user 表
        // true：@RequestMapping("/sys-user")，false：@RequestMapping("/sysUser")
        // 如果包配置设置了模块名称，则 url 前会加上模块名称
        strategy.setControllerMappingHyphenStyle(false);
        // 公共父类 "你自己的父类控制器,没有就不用设置!"
        // strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");

        mpg.setStrategy(strategy);
    }


    /**
     * 全局配置
     * @return
     */
    private static void globalConfig() {
        GlobalConfig gc = new GlobalConfig();
        // 是否覆盖已有文件
        gc.setFileOverride(false);
        // 设置生成文件的输出目录
        String outputDir = Paths.get(projectPath, "/src/main/java").toString();
        gc.setOutputDir(outputDir);
        // 作者姓名
        gc.setAuthor(author);
        // 是否打开输出目录
        gc.setOpen(true);
        // 实体属性 Swagger2 注解
        gc.setSwagger2(true);
        // 开启 BaseResultMap
        gc.setBaseResultMap(true);
        // 开启 baseColumnList
        gc.setBaseColumnList(true);
        // 生成的 Service 接口默认有 I 前缀，这里去掉
        gc.setServiceName("%sService");
        mpg.setGlobalConfig(gc);
    }
}
